//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
// Developed by Minigraph
//
// Author:  James Stanard 
//          Julia Careaga
//

#include "ParticleUpdateCommon.hlsli"
#include "ParticleUtility.hlsli"

cbuffer CB0 : register(b0)
{
    float gElapsedTime;
};

StructuredBuffer< ParticleSpawnData > g_ResetData : register( t0 );
StructuredBuffer< ParticleMotion > g_InputBuffer : register( t1 );
RWStructuredBuffer< ParticleVertex > g_VertexBuffer : register( u0 );
RWStructuredBuffer< ParticleMotion > g_OutputBuffer : register( u2 );

[RootSignature(Particle_RootSig)]
[numthreads(64, 1, 1)]
void main( uint3 DTid : SV_DispatchThreadID )
{
    if (DTid.x >= MaxParticles)
        return;

    ParticleMotion ParticleState = g_InputBuffer[ DTid.x ];
    ParticleSpawnData rd = g_ResetData[ ParticleState.ResetDataIndex ];

    // Update age.  If normalized age exceeds 1, the particle does not renew its lease on life.
    ParticleState.Age += gElapsedTime * rd.AgeRate;
    if (ParticleState.Age >= 1.0)
        return;

    // Update position.  Compute two deltas to support rebounding off the ground plane.
    float StepSize = (ParticleState.Position.y > 0.0 && ParticleState.Velocity.y < 0.0) ?
        min(gElapsedTime, ParticleState.Position.y / -ParticleState.Velocity.y) : gElapsedTime;

    ParticleState.Position += ParticleState.Velocity * StepSize;
    ParticleState.Velocity += Gravity * ParticleState.Mass * StepSize;

    // Rebound off the ground if we didn't consume all of the elapsed time
    StepSize = gElapsedTime - StepSize;
    if (StepSize > 0.0)
    {
        ParticleState.Velocity = reflect(ParticleState.Velocity, float3(0, 1, 0)) * Restitution;
        ParticleState.Position += ParticleState.Velocity * StepSize;
        ParticleState.Velocity += Gravity * ParticleState.Mass * StepSize;
    }

    // The spawn dispatch will be simultaneously adding particles as well.  It's possible to overflow.
    uint index = g_OutputBuffer.IncrementCounter();	
    if (index >= MaxParticles)
        return;

    g_OutputBuffer[index] = ParticleState;

    //
    // Generate a sprite vertex
    //

    ParticleVertex Sprite;

    Sprite.Position = ParticleState.Position;
    Sprite.TextureID = TextureID;

    // Update size and color
    Sprite.Size = lerp(rd.StartSize, rd.EndSize, ParticleState.Age);
    Sprite.Color = lerp(rd.StartColor, rd.EndColor, ParticleState.Age);

    // ...Originally from Reflex...
    // Use a trinomial to smoothly fade in a particle at birth and fade it out at death.
    Sprite.Color *= ParticleState.Age * (1.0 - ParticleState.Age) * (1.0 - ParticleState.Age) * 6.7;

    g_VertexBuffer[ g_VertexBuffer.IncrementCounter() ] = Sprite;
}
